Wrapping a Cplusplus class in Python/ru

Other languages:
Данная статья нуждается в развитии. Пожалуйста, внесите в неё вклад своими знаниями!

Механизм работы

FreeCAD использует собственную систему на основе XML для создания Python-обёрток для классов C++. Чтобы обернуть класс C++ для использования в Python, необходимо создать два файла вручную, и два файла автоматически генерируются системой сборки CMake (в дополнение к заголовочным файлам C++ и файлам реализации класса).

Вам нужно создать:

Отредактируйте соответствующий файл CMakeLists.txt, чтобы добавить ссылки на эти два файла. На основе XML-файла система сборки создаст:

XML-файл описания Класса

XML-файл [YourClass]Py.xml содержит информацию о функциях и атрибутах, которые реализует класс Python, а также пользовательскую документацию для этих элементов, которая отображается в консоли Python FreeCAD.

В этом примере мы рассмотрим обёртку для класса Axis C++. Файл описания XML начинается со следующего:

<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
    <PythonExport
        Father="PyObjectBase"
        Name="AxisPy"
        Twin="Axis"
        TwinPointer="Axis"
        Include="Base/Axis.h"
        FatherInclude="Base/PyObjectBase.h"
        Namespace="Base"
        Constructor="true"
        Delete="true"
        FatherNamespace="Base">
    <Documentation>
        <Author Licence="LGPL" Name="Juergen Riegel" EMail="FreeCAD@juergen-riegel.net" />
        <UserDocu>User documentation here
        </UserDocu>
        <DeveloperDocu>Developer documentation here</DeveloperDocu>
    </Documentation>

После этой преамбулы приводится список методов и атрибутов. Формат метода следующий:

<Methode Name="move">
    <Documentation>
        <UserDocu>
        move(Vector)
        Move the axis base along the vector
        </UserDocu>
    </Documentation>
</Methode>

Формат атрибута следующий:

<Attribute Name="Direction" ReadOnly="false">
    <Documentation>
        <UserDocu>Direction vector of the Axis</UserDocu>
    </Documentation>
    <Parameter Name="Direction" Type="Object" />
</Attribute>

Если атрибут "ReadOnly" имеет значение false, вы должны предоставить функцию getter и setter. Если значение равно true, то допускается только getter. В этом случае мы должны будем предоставить две функции в файле реализации C++:

Py::Object AxisPy::getDirection(void) const

и:

void AxisPy::setDirection(Py::Object arg)

Реализация файла Cplusplus

Файл реализации C++ [YourClass]PyImp.cpp обеспечивает "клей", который соединяет структуры C++ и Python вместе, эффективно переводя с одного языка на другой. Система FreeCAD C++-to-Python предоставляет ряд классов C++, которые соотносятся с соответствующими типами Python. Наиболее фундаментальным из них является класс Py::Object - редко создаваемый напрямую, этот класс обеспечивает основу дерева наследования и используется в качестве возвращаемого типа для любой функции, возвращающей данные Python.

Включаемые Файлы

Ваш файл реализации на C++ будет включать следующие файлы:

#include "PreCompiled.h"

#include "[YourClass].h"

// Inclusion of the generated files (generated out of [YourClass]Py.xml)
#include "[YourClass]Py.h"
#include "[YourClass]Py.cpp"

Конечно, вы можете включить и другие заголовки C++, необходимые для работы вашего кода.

Конструктор

Ваша реализация на C++ должна содержать определение функции PyInit: например, для обёртки класса Axis это

int AxisPy::PyInit(PyObject* args, PyObject* /*kwd*/)

Внутри этой функции вам, скорее всего, понадобится разобрать входящие в конструктор аргументы: наиболее важной функцией для этого является предоставляемая Python PyArg_ParseTuple. Она принимает переданный список аргументов, дескриптор для ожидаемых аргументов, которые она должна разобрать, а также информацию о типе и месте хранения результатов разбора. Например:

PyObject* d;
    if (PyArg_ParseTuple(args, "O!O", &(Base::VectorPy::Type), &o,
                                      &(Base::VectorPy::Type), &d)) {
        // NOTE: The first parameter defines the base (origin) and the second the direction.
        *getAxisPtr() = Base::Axis(static_cast<Base::VectorPy*>(o)->value(),
                                   static_cast<Base::VectorPy*>(d)->value());
        return 0;
    }

Полный список спецификаторов формата приведён в Документации Python C API. Обратите внимание, что также определено несколько связанных функций, которые позволяют использовать ключевые слова и т. д. Полный набор следующий:

PyAPI_FUNC(int) PyArg_Parse (PyObject *, const char *, ...);
PyAPI_FUNC(int) PyArg_ParseTuple (PyObject *, const char *, ...);
PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords (PyObject *, PyObject *, const char *, char **, ...);
PyAPI_FUNC(int) PyArg_VaParse (PyObject *, const char *, va_list);
PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords (PyObject *, PyObject *, const char *, char **, va_list);

Ссылки